#include <stdlib.h>
#include <signal.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <errno.h>

#include "thread_pool.h"
#include "seats.h"
#include "util.h"

#define BUFSIZE 1024
#define FILENAMESIZE 100

void shutdown_server(int);

int listenfd; // a file descriptor for a socket
pool_t* threadpool; // defined in thread_pool.c, head pointer to a struct/ linked list

int main(int argc, char *argv[])
{
    /*******************************NETWORK CONNECTION CODE BELOW*********************************/

    int flag, num_seats = 20;
    int connfd = 0; // connected file descriptor
    struct sockaddr_in serv_addr; 
    // server sock addr containing the ip and the port number CSAPP P902

    char send_buffer[BUFSIZE];
    
    listenfd = 0; // meaningless?

    int server_port = 8080;

    if (argc > 1)
        num_seats = atoi(argv[1]); // arg[0] is the name the exe file

    if (server_port < 1500)
    {
        fprintf(stderr,"INVALID PORT NUMBER: %d; can't be < 1500\n",server_port);
        exit(-1);
    }

    if (signal(SIGINT, shutdown_server) == SIG_ERR) // by enter crtl c, shutdown the server
        printf("Issue registering SIGINT handler");

    listenfd = socket(AF_INET, SOCK_STREAM, 0); 
    // create and return a socket descriptor
    // int socket(int domain, int type, int protocol);
    // socket() creates an endpoint for communication and returns a file descriptor of a new socket
    // On error, -1 is returned
    if (listenfd < 0) { // listenfd is like a number associated to a file
        perror("Socket");
        exit(errno);
    }

    printf("Established Socket: %d\n", listenfd);
    flag = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
    // int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
    // level = SOL_SOCKET -> an option applying for API level
    // by default the after restarting, the server denies request from the client for 30 seconds
    // setsockopt removes this limitation?

    // initialize server address and send_buffer to zero
    memset(&serv_addr,'0', sizeof(serv_addr));
    memset(send_buffer,'0', sizeof(send_buffer));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // receive request from any ip addr
    serv_addr.sin_port = htons(server_port); // receive request from the specified port

    // bind to socket, associate the addr to the socket descriptor
    if (bind(listenfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) != 0) {
        perror("socket--bind");
        exit(errno);
    }

    listen(listenfd, 10);
    // converts active socket to a listening socket
    // int listen(int sockfd, int backlog);
    // 10 = the maximum length to which the queue of pending connections for sockfd may grow

    /********************************NETWORK CONNECTION CODE ABOVE*********************************/

    load_seats(num_seats); // create a linked list of seats

    // create threadpool and set the number of threads and size of the task queue
    threadpool = pool_create(MAX_THREADS, TASK_QUEUE_SIZE);

    while(1) { // loop forever
        connfd = accept(listenfd, (struct sockaddr*) NULL, NULL); // accept clients' connection requests
        pool_add_task(threadpool, connfd); // add the task to the task queue
    }
}

void shutdown_server(int signo)
{
    pool_destroy(threadpool); // in threadpool.c
    unload_seats(); // in seat.c
    close(listenfd);
    exit(0);
}
